package com.sfjswl.fp.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.sfjswl.fp.common.dto.RuleConf;
import com.sfjswl.fp.common.dto.RuleInfoDto;
import com.sfjswl.fp.common.enums.TimeTypeEnum;
import com.sfjswl.fp.core.builder.RuleHandlerBuilder;
import com.sfjswl.fp.core.context.RuleContext;
import com.sfjswl.fp.core.context.RulePack;
import com.sfjswl.fp.core.handler.RuleHandler;
import com.sfjswl.fp.core.utils.RuleExecutor;
import com.sfjswl.fp.core.utils.RuleTimeUtils;
import io.vavr.collection.*;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.sql.SQLOutput;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Supplier;

@Slf4j
public class Dispatcher {

    public interface ObtainRule<T, R> {
        R apply(T t);
    }

    public Optional<RuleContext> syncDispatcher(RulePack pack, RuleInfoDto ruleInfo) {
        return dispatcher(pack, ruleInfo, false);
    }

    @SneakyThrows
    public <E> Optional<E> dispatcher(RulePack pack, RuleInfoDto ruleInfo, boolean isAsync) {
        if (checkPackAndRuleNode(pack, ruleInfo)) {
            return Optional.empty();
        }
        RuleHandler handler = RuleHandlerBuilder.build(ruleInfo);
        if (handler == null) {
            return Optional.empty();
        }
        RuleContext ctx = new RuleContext(handler.getRuleId(), pack);
        if (isAsync) {
            Future<RuleContext> future = RuleExecutor.submitHandler(handler, ctx);
            //noinspection unchecked
            return Optional.of((E) future.get());
        }
        handler.handle(ctx);
        //noinspection unchecked
        return Optional.of((E) ctx);
    }

    /**
     * 规则执行器
     *
     * @param pack         需校验入参
     * @param ruleSupplier 规则列表（根结点的parentId需要为空）
     * @return 返回适配规则的数据
     */
    public RuleContext dispatch(RulePack pack, Supplier<Array<RuleConf>> ruleSupplier) {
        Array<RuleConf> ruleConfList = ruleSupplier.get();
        List<Tree.Node<RuleConf>> nodes = ruleConfList
                .sorted((o1, o2) -> Integer.compare(o2.getSequence(), o1.getSequence()))
                .toTree(RuleConf::getId, RuleConf::getParentId);
        if (nodes.isEmpty()) {
            return null;
        }
        RuleContext context = new RuleContext(0, pack);
        nodes.get().traverse(Tree.Order.IN_ORDER).reduce(
                (currNode, nextNode) -> {
                    RuleConf curr = currNode.get();
                    if (RuleTimeUtils.timeDisable(TimeTypeEnum.getEnumDefaultNone(curr.getTimeType()), context.getPack().getRequestTime(), curr.getStart(), curr.getEnd())) {
                        return new Tree.Node<>(nextNode.get(), nextNode.getChildren());
                    }
                    String s = currNode.get().getName() + "||" + nextNode.get().getName();
                    return new Tree.Node<>(nextNode.get(), nextNode.getChildren());
                }
        );
        return null;
    }

    private boolean checkPackAndRuleNode(RulePack pack, RuleInfoDto base) {
        if (pack == null
                || base == null
                || base.getRuleConfDtos() == null
                || base.getRuleConfDtos().size() == 0) {
            return true;
        }
        if (pack.getRuleId() > 0) {
            return false;
        }
        return pack.getScene() == null || pack.getScene().isEmpty();
    }

    public static void main(String[] args) throws JsonProcessingException, ExecutionException, InterruptedException {
//        String ruleBaseStr = "{\"id\": 1,\"confId\": 1}";
//        String ruleConfStr = "[{\"id\":1,\"sonIds\":\"2,3\",\"type\":4},{\"id\":2,\"sonIds\":\"8,5\",\"type\":1},{\"id\":3,\"sonIds\":\"6,9\",\"type\":1,\"timeType\":5,\"start\":1662566440000},{\"id\":4,\"type\":5,\"confName\":\"com.sfjswl.fp.core.flow.ScoreFlow2\"},{\"id\":5,\"type\":6,\"confName\":\"com.sfjswl.fp.core.result.AmountResult\",\"confField\":\"{\\\"key\\\": \\\"uuid\\\", \\\"value\\\": 5}\"},{\"id\":6,\"type\":5,\"confName\":\"com.sfjswl.fp.core.flow.ScoreFlow\",\"confField\":\"{\\\"score\\\": 80, \\\"key\\\": \\\"cust\\\"}\"},{\"id\":7,\"type\":6,\"confName\":\"com.sfjswl.fp.core.result.AmountResult2\"},{\"id\":8,\"type\":5,\"confName\":\"com.sfjswl.fp.core.flow.ScoreFlow\",\"confField\":\"{\\\"score\\\": 100, \\\"key\\\": \\\"cust\\\"} \"},{\"id\":9,\"type\":6,\"confName\":\"com.sfjswl.fp.core.result.PointResult\",\"confField\":\"{\\\"key\\\": \\\"uuid\\\", \\\"value\\\": 10}\"},{\"id\":10,\"type\":0},{\"id\":11,\"type\":0}]";
//        String packStr = "{\"ruleId\":1,\"requestTime\":1662601782288,\"roam\":{\"cust\":\"110\",\"uuid\":1}}";
//        RuleInfoDto ruleInfoDto = JacksonUtils.readJson(ruleBaseStr, RuleInfoDto.class);
//        RulePack pack = JacksonUtils.readJson(packStr, RulePack.class);
//        Collection<RuleConfDto> ruleConfDtos = JacksonUtils.readJson(ruleConfStr, new TypeReference<Collection<RuleConfDto>>() {
//        });
//        ruleInfoDto.setRuleConfDtos(ruleConfDtos);
//        Optional<RuleContext> optional = new Dispatcher().dispatcher(pack, ruleInfoDto, false);
//        System.out.println(optional);
        List<DataT> list = List.of(new DataT(1L, null, 0, "数据1", "{\"num\": 1}"),
                new DataT(21L, 1L, 1, "数据2-1", "{\"num\": 21}"),
                new DataT(22L, 1L, 0, "数据2-2", "{\"num\": 22}"),
                new DataT(33L, 22L, 0, "数据3-3", "{\"num\": 33}"),
                new DataT(34L, 22L, 1, "数据3-4", "{\"num\": 34}")).sorted(new Comparator<DataT>() {
            @Override
            public int compare(DataT o1, DataT o2) {
                return Long.compare(o2.getId(), o1.getId());
            }
        });
        List<Tree.Node<DataT>> nodeList = Tree.build(list, DataT::getId, DataT::getParentId);
        System.out.println(nodeList.get().draw());
        Tree.Node<DataT> dataTS1 = nodeList.get().traverse(Tree.Order.POST_ORDER)
                .reduce(
                        (dataTS, dataTS2) -> {
                            String s = dataTS.get().getName() + "+" + dataTS2.get().getName();
                            Tree.Node<DataT> dataTS3 = new Tree.Node<>(new DataT(0L, dataTS2.get().getParentId(), 0, s, null), dataTS2.getChildren());
                            return dataTS3;
                        }
                );
        System.out.println(dataTS1);
    }

    @Data
    static class DataT {
        private final Long id;
        private final Long parentId;
        private final int index;
        private final String name;
        private final String conf;

        public DataT(Long id, Long parentId, int index, String name, String conf) {
            this.id = id;
            this.parentId = parentId;
            this.index = index;
            this.name = name;
            this.conf = conf;
        }
    }
}
